import is from './is';
import delegate from './delegate';

/**
 * Validates all params and calls the right
 * listener function based on its target type.
 *
 * @param {String|HTMLElement|HTMLCollection|NodeList} target
 * @param {String} type
 * @param {Function} callback
 * @return {Object}
 */
function listen(target, type, callback) {
  if (!target && !type && !callback) {
    throw new Error('Missing required arguments');
  }

  if (!is.string(type)) {
    throw new TypeError('Second argument must be a String');
  }

  if (!is.fn(callback)) {
    throw new TypeError('Third argument must be a Function');
  }

  if (is.node(target)) {
    return listenNode(target, type, callback);
  } else if (is.nodeList(target)) {
    return listenNodeList(target, type, callback);
  } else if (is.string(target)) {
    return listenSelector(target, type, callback);
  } else {
    throw new TypeError(
      'First argument must be a String, HTMLElement, HTMLCollection, or NodeList',
    );
  }
}

/**
 * Adds an event listener to a HTML element
 * and returns a remove listener function.
 *
 * @param {HTMLElement} node
 * @param {String} type
 * @param {Function} callback
 * @return {Object}
 */
function listenNode(node, type, callback) {
  node.addEventListener(type, callback);

  return {
    destroy() {
      node.removeEventListener(type, callback);
    },
  };
}

/**
 * Add an event listener to a list of HTML elements
 * and returns a remove listener function.
 *
 * @param {NodeList|HTMLCollection} nodeList
 * @param {String} type
 * @param {Function} callback
 * @return {Object}
 */
function listenNodeList(nodeList, type, callback) {
  Array.prototype.forEach.call(nodeList, function (node) {
    node.addEventListener(type, callback);
  });

  return {
    destroy() {
      Array.prototype.forEach.call(nodeList, function (node) {
        node.removeEventListener(type, callback);
      });
    },
  };
}

/**
 * Add an event listener to a selector
 * and returns a remove listener function.
 *
 * @param {String} selector
 * @param {String} type
 * @param {Function} callback
 * @return {Object}
 */
function listenSelector(selector, type, callback) {
  return delegate(document.body, selector, type, callback);
}

export default listen;
